import 'dart:math';

import 'package:eplorex/models/date_header.dart';
import 'package:eplorex/models/message/chat_message_type.dart' as types;
import 'package:eplorex/pages/message/state/inherited_chat_theme.dart';
import 'package:eplorex/pages/message/state/inherited_l10n.dart';
import 'package:eplorex/widgets/input/input.dart';
import 'package:eplorex/widgets/message/unread_header.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
import 'package:oktoast/oktoast.dart';
import 'package:scroll_to_index/scroll_to_index.dart';

import '../../../models/bubble_rtl_alignment.dart';
import '../../../models/emoji_enlargement_behavior.dart';
import '../../../models/message_spacer.dart';
import '../../../models/preview_image.dart';
import '../../../models/unread_header_data.dart';
import '../../../widgets/message/message.dart';
import '../../../widgets/message/text_message.dart';
import '../../../widgets/message/typing_indicator.dart';
import '../state/chat_l10n.dart';
import '../state/chat_theme.dart';
import '../state/inherited_user.dart';
import '../wx_popup_menu_widget.dart';
import 'chat_list.dart';
import 'chat_util.dart';

/// Keep track of all the auto scroll indices by their respective message's id to allow animating to them.
final Map<String, int> chatMessageAutoScrollIndexById = {};

class Chat extends StatefulWidget {
  /// List of [types.Message] to render in the chat widget.
  final List<types.Message> messages;

  /// See [InheritedUser.user].
  final types.User user;

  /// Allows you to change what the user sees when there are no messages.
  /// `emptyChatPlaceholder` and `emptyChatPlaceholderTextStyle` are ignored
  /// in this case.
  final Widget? emptyState;

  /// Time (in ms) between two messages when we will render a date header.
  /// Default value is 15 minutes, 900000 ms. When time between two messages
  /// is higher than this threshold, date header will be rendered. Also,
  /// not related to this value, date header will be rendered on every new day.
  final int dateHeaderThreshold;

  /// Show user names for received messages. Useful for a group chat. Will be
  /// shown only on text messages.
  final bool showUserNames;

  /// Time (in ms) between two messages when we will visually group them.
  /// Default value is 1 minute, 60000 ms. When time between two messages
  /// is lower than this threshold, they will be visually grouped.
  final int groupMessagesThreshold;

  /// See [Input.options].
  final InputOptions inputOptions;

  /// Controls if and how the chat should scroll to the newest unread message.
  final ScrollToUnreadOptions scrollToUnreadOptions;

  /// Custom date header builder gives ability to customize date header widget.
  final Widget Function(DateHeader)? dateHeaderBuilder;

  /// See [Input.onSendPressed].
  final void Function(types.PartialText) onSendPressed;

  /// Chat theme. Extend [ChatTheme] class to create your own theme or use
  /// existing one, like the [DefaultChatTheme]. You can customize only certain
  /// properties, see more here [DefaultChatTheme].
  final ChatTheme theme;

  /// Localized copy. Extend [ChatL10n] class to create your own copy or use
  /// existing one, like the default [ChatL10nEn]. You can customize only
  /// certain properties, see more here [ChatL10nEn].
  final ChatL10n l10n;

  /// See [ChatList.bottomWidget]. For a custom chat input
  /// use [customBottomWidget] instead.
  final Widget? listBottomWidget;

  /// Allows you to replace the default Input widget e.g. if you want to create a channel view. If you're looking for the bottom widget added to the chat list, see [listBottomWidget] instead.
  final Widget? customBottomWidget;

  /// See [Message.showUserAvatars].
  final bool showUserAvatars;

  /// Called when user taps on background.
  final VoidCallback? onBackgroundTap;

  /// Called when user makes a long press on any message.
  final void Function(BuildContext context, types.Message)? onMessageLongPress;

  /// Width ratio for message bubble.
  final double messageWidthRatio;

  /// See [Message.slidableMessageBuilder].
  final Widget Function(types.Message, Widget msgWidget)?
      slidableMessageBuilder;

  /// See [Message.textMessageOptions].
  final TextMessageOptions textMessageOptions;

  /// Used to show typing users with indicator. See [TypingIndicatorOptions].
  final TypingIndicatorOptions typingIndicatorOptions;

  /// See [Input.onAttachmentPressed].
  final VoidCallback? onAttachmentPressed;

  /// See [Input.isAttachmentUploading].
  final bool? isAttachmentUploading;

  /// See [Message.onMessageTap].
  final void Function(BuildContext context, types.Message)? onMessageTap;

  /// See [Message.onPreviewDataFetched].
  final void Function(types.TextMessage, types.PreviewData)?
      onPreviewDataFetched;

  Chat({
    required this.messages,
    required this.user,
    this.dateHeaderThreshold = 900000,
    required this.showUserNames,
    this.onAttachmentPressed,
    this.onBackgroundTap,
    this.onPreviewDataFetched,
    this.inputOptions = const InputOptions(),
    this.isAttachmentUploading,
    this.onMessageTap,
    this.customBottomWidget,
    this.showUserAvatars = true,
    this.textMessageOptions = const TextMessageOptions(),
    this.messageWidthRatio = 0.72,
    this.emptyState,
    this.slidableMessageBuilder,
    this.listBottomWidget,
    this.groupMessagesThreshold = 60000,
    this.onMessageLongPress,
    this.dateHeaderBuilder,
    required this.scrollToUnreadOptions,
    required this.onSendPressed,
    this.typingIndicatorOptions = const TypingIndicatorOptions(),
    this.theme = const DefaultChatTheme(),
    this.l10n = const ChatL10nEn(),
  });

  @override
  State<Chat> createState() => _ChatState();
}

class _ChatState extends State<Chat> {
  static const String _unreadHeaderId = 'unread_header_id';
  List<Object> _chatMessages = [];
  List<PreviewImage> _gallery = [];
  PageController? _galleryPageController;

  ValueNotifier<bool>? _backgroundTap;

  late final AutoScrollController _scrollController;

  @override
  void initState() {
    super.initState();
    // 用户自定暂时不考虑,所以不暴露外部接口
    _scrollController = AutoScrollController();
    _backgroundTap = new ValueNotifier(true);
    didUpdateWidget(widget);
  }

  Widget _emptyStateBuilder() =>
      widget.emptyState ??
      Container(
        alignment: Alignment.center,
        margin: const EdgeInsets.symmetric(
          horizontal: 24,
        ),
        child: Text(
          widget.l10n.emptyChatPlaceholder,
          style: widget.theme.emptyChatPlaceholderTextStyle,
          textAlign: TextAlign.center,
        ),
      );

  @override
  Widget build(BuildContext context) => InheritedUser(
        user: widget.user,
        child: InheritedChatTheme(
          theme: widget.theme,
          child: InheritedL10n(
            l10n: widget.l10n,
            child: Stack(
              children: [
                Container(
                  color: widget.theme.backgroundColor,
                  child: Column(
                    children: [
                      Flexible(
                          child: widget.messages.isEmpty
                              ? SizedBox.expand(
                                  child: _emptyStateBuilder(),
                                )
                              : GestureDetector(
                                  onTap: () {
                                    FocusManager.instance.primaryFocus
                                        ?.unfocus();
                                    widget.onBackgroundTap?.call();
                                    _backgroundTap!.value =
                                        !_backgroundTap!.value;
                                  },
                                  child: LayoutBuilder(
                                    builder: (
                                      BuildContext context,
                                      BoxConstraints constraints,
                                    ) =>
                                        ChatList(
                                      bottomWidget: widget?.listBottomWidget,
                                      bubbleRtlAlignment:
                                          BubbleRtlAlignment.left,
                                      isLastPage: true,
                                      itemBuilder: (Object item, int? index) =>
                                          _messageBuilder(
                                        item,
                                        constraints,
                                        index,
                                      ),
                                      items: _chatMessages,
                                      typingIndicatorOptions:
                                          widget.typingIndicatorOptions,
                                      useTopSafeAreaInset: true,
                                      scrollController: _scrollController,
                                    ),
                                  ),
                                )),
                      widget.customBottomWidget ??
                          Input(
                            backgroundTap: _backgroundTap,
                            isAttachmentUploading: widget.isAttachmentUploading,
                            onAttachmentPressed: widget.onAttachmentPressed,
                            onSendPressed: widget.onSendPressed,
                            options: widget.inputOptions,
                          )
                    ],
                  ),
                )
              ],
            ),
          ),
        ),
      );

  /// Updates the [chatMessageAutoScrollIndexById] mapping with the latest messages.
  void _refreshAutoScrollMapping() {
    chatMessageAutoScrollIndexById.clear();
    var i = 0;
    for (final object in _chatMessages) {
      if (object is UnreadHeaderData) {
        chatMessageAutoScrollIndexById[_unreadHeaderId] = i;
      } else if (object is Map<String, Object>) {
        final message = object['message']! as types.Message;
        chatMessageAutoScrollIndexById[message.id] = i;
      }
      i++;
    }
  }


  /// Only scroll to first unread if there are messages and it is the first open.
  void _maybeScrollToFirstUnread() {
    // if (widget.scrollToUnreadOptions.scrollOnOpen &&
    //     _chatMessages.isNotEmpty &&
    //     !_hadScrolledToUnreadOnOpen) {
    //   WidgetsBinding.instance.addPostFrameCallback((_) async {
    //     if (mounted) {
    //       await Future.delayed(widget.scrollToUnreadOptions.scrollDelay);
    //       scrollToUnreadHeader();
    //     }
    //   });
    //   _hadScrolledToUnreadOnOpen = true;
    // }
  }

  @override
  void didUpdateWidget(covariant Chat oldWidget) {
    super.didUpdateWidget(oldWidget);

    if (widget.messages.isNotEmpty) {
      final result = calculateChatMessages(widget.messages, widget.user,
          dateHeaderThreshold: widget.dateHeaderThreshold,
          showUserNames: widget.showUserNames,
          dateFormat:DateFormat('MM月dd日') ,
          groupMessagesThreshold: widget.groupMessagesThreshold);

      _chatMessages = result[0] as List<Object>;
      _gallery = result[1] as List<PreviewImage>;

      _refreshAutoScrollMapping();
    }
  }

  @override
  void dispose() {
    _galleryPageController?.dispose();
    _scrollController.dispose();
    super.dispose();
  }

  Widget _messageBuilder(
      Object object, BoxConstraints constraints, int? index) {
    if (object is MessageSpacer) {
      return SizedBox(
        height: object.height,
      );
    } else if (object is UnreadHeaderData) {
      return UnreadHeader(
        marginTop: object.marginTop,
      );
    }else if(object is DateHeader){
      return widget.dateHeaderBuilder?.call(object) ??
          Container(
            alignment: Alignment.center,
            margin: widget.theme.dateDividerMargin,
            child: Text(
              object.text,
              style: widget.theme.dateDividerTextStyle,
            ),
          );
    } else {
      final map = object as Map<String, Object>;
      final message = map['message']! as types.Message;
      final Widget messageWidget;

      final maxWidth = widget.theme.messageMaxWidth;
      final messageWidth =
          widget.showUserAvatars && message.author.uid != widget.user.uid
              ? min(constraints.maxWidth * widget.messageWidthRatio, maxWidth)
                  .floor()
              : min(
                  constraints.maxWidth * (widget.messageWidthRatio + 0.06),
                  maxWidth,
                ).floor();

      final Widget msgWidget = Message(
        emojiEnlargementBehavior: EmojiEnlargementBehavior.never,
        hideBackgroundOnEmojiMessages: true,
        message: message,
        messageWidth: messageWidth,
        roundBorder: true,
        showAvatar: true,
        showName: false,
        showStatus: true,
        showUserAvatars: true,
        isLeftStatus: true,
        textMessageOptions: widget.textMessageOptions,
        usePreviewData: false,
        onMessageLongPress: (context, types.Message msg){
          widget.onMessageLongPress?.call(context,msg);
        },
      );
      messageWidget = widget.slidableMessageBuilder == null
          ? msgWidget
          : widget.slidableMessageBuilder!(message, msgWidget);
      return AutoScrollTag(
        controller: _scrollController,
        index: index ?? -1,
        key: Key('scroll-${message.id}'),
        highlightColor: widget.theme.highlightMessageColor,
        child: messageWidget,
      );
    }
  }
}
